#include <iostream>
#include <queue>
#include <vector>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

//<--- 全局变量 --->
const int k_Shelf = 5;              //货架缓冲区最大容量
const int k_NumBaker = 2;           //面包师数量
const int k_NumCustomer = 4;        //消费者数量
const int k_BreadPerBaker = 10;     //每个面包师做的面包数量
const int k_BreadPerCustomer = 5;   //每个顾客消费的面包数量

//<--- 共享资源 --->
std::queue<int> g_Shelf;                                      //共享货架队列
pthread_mutex_t g_Mutex = PTHREAD_MUTEX_INITIALIZER;          //临界资源锁
sem_t g_FilledSlots;                                          //货架满信号量
sem_t g_EmptySlots;                                           //货架空信号量

//面包师线程方法
void* ThreadBakerFunc(void* args){
    //获取面包师ID
    long ID = reinterpret_cast<long>(args);
    
    //制作10个面包
    for(int i = 1; i <= k_BreadPerBaker; i++){
        //获取信号量
        sem_wait(&g_EmptySlots); //通过说明货架有空位，并执行--

        //加锁
        pthread_mutex_lock(&g_Mutex);

        //可以制作面包
        int BreadID = static_cast<int>(ID) * 100 + i;
        //加入货架
        g_Shelf.push(BreadID);
        std::cout << "面包师：" << ID << "，考好了面包[" << BreadID << "]。货架上还有 "
        << g_Shelf.size() << " 个面包" << std::endl;

        //解锁退出临界区
        pthread_mutex_unlock(&g_Mutex);

        //制作好了就让货架满信号量++，让消费者消费
        sem_post(&g_FilledSlots);

        //模拟烤面包耗时，提高并发度
        usleep(1000 * 300);
    }
    return nullptr;
}

//消费者线程方法
void* ThreadCustomerFunc(void* args){
    //获取消费者ID
    long ID = reinterpret_cast<long>(args);

    //购买面包
    for(int i = 1; i <= k_BreadPerCustomer; i++){
        //获取信号量
        sem_wait(&g_FilledSlots);//获取成功说明货架上有面包，并且做--，面包为0就阻塞

        //加锁
        pthread_mutex_lock(&g_Mutex);

        //消费者可以买面包
        int BreadID = g_Shelf.front();
        g_Shelf.pop();

        std::cout << "消费者：" << ID << "，买到面包[" << BreadID << "]。货架上还有 "
        << g_Shelf.size() << " 个面包" << std::endl;

        //解锁
        pthread_mutex_unlock(&g_Mutex);

        //购买了面包，货架有空位了,让空信号量++
        sem_post(&g_EmptySlots);

        //模拟购买后操作
        usleep(1000 * 1000);
    }
    return nullptr;
}


int main(){
    //<--- 初始化变量 --->
    std::vector<pthread_t> Threads(k_NumBaker + k_NumCustomer); //总线程数
    sem_init(&g_EmptySlots, 0, k_Shelf);                        //开始货架全空
    sem_init(&g_FilledSlots, 0, 0);                             //开始面包没有
    
    std::cout << "-------------------面包店开门了-------------------" << std::endl;

    //创建面包师线程，i使用long避免传参形成数据丢失
    for(long i = 1; i <= k_NumBaker; i++){
        pthread_create(&Threads[i - 1], nullptr, ThreadBakerFunc, (void*)i);
    }
    
    //创建消费者线程
    for(long i = 1; i <= k_NumCustomer; i++){
        pthread_create(&Threads[k_NumBaker + i - 1], nullptr, ThreadCustomerFunc, (void*)i);
    }

    //回收线程
    for(long i = 1; i <= k_NumBaker + k_NumCustomer; i++){
        pthread_join(Threads[i - 1], nullptr);
    }

    //销毁锁、信号量资源
    pthread_mutex_destroy(&g_Mutex);
    sem_destroy(&g_FilledSlots);
    sem_destroy(&g_EmptySlots);

    std::cout << "-------------------面包店关门了-------------------" << std::endl;

    return 0;
}